package com.cs.framework.jdbc.mybatis3.builder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.builder.IncompleteElementException;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.builder.xml.XMLMapperEntityResolver;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.parsing.XPathParser;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.session.Configuration;
import org.springframework.core.io.Resource;

import com.cs.framework.common.util.IOUtils;
import com.cs.framework.jdbc.mybatis3.exception.MyBatisExceptionHelper;

/**
 * 构建SQL
 * 
 * @author hedongzhou
 * 
 */
public class SqlBuilder {

	private final Log logger = LogFactory.getLog(getClass());

	private Configuration configuration;

	public SqlBuilder(Configuration configuration) {
		this.configuration = configuration;
	}

	/**
	 * 根据XML构建SQL
	 * 
	 * @param pathPattern
	 *            xml路径
	 * @throws IOException
	 */
	public void build(String pathPattern) {
		// 加载资源
		Resource[] resources = IOUtils.loadResource(pathPattern);

		if (resources != null) {
			for (Resource resource : resources) {
				InputStream inputStream = null;
				try {
					inputStream = resource.getInputStream();
				} catch (IOException e) {
					logger.error(e.getMessage(), e);
					MyBatisExceptionHelper.throwJdbcException("M001", resource.getFilename());
				}

				if (inputStream != null) {
					// 解析
					parse(inputStream, resource.getFilename());
				}
			}
		}
	}

	/**
	 * 解析单个XML
	 * 
	 * @param inputStream
	 * @param resourceName
	 */
	public void parse(InputStream inputStream, String resourceName) {
		MapperBuilderAssistant builderAssistant = new MapperBuilderAssistant(configuration, resourceName);
		XPathParser parser = new XPathParser(inputStream, false, configuration.getVariables(),
				new XMLMapperEntityResolver());

		XNode context = parser.evalNode("/sqls");
		try {
			String namespace = context.getStringAttribute("id");
			if (namespace.equals("")) {
				MyBatisExceptionHelper.throwJdbcException("M002");
			}

			builderAssistant.setCurrentNamespace(namespace);
			List<XNode> list = context.evalNodes("sql");

			for (XNode node : list) {
				try {
					parseSql(builderAssistant, node);
				} catch (IncompleteElementException e) {
					MyBatisExceptionHelper.throwJdbcException("M004", resourceName, node.getStringAttribute("id"));
				}
			}
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			MyBatisExceptionHelper.throwJdbcException(e, "M003", resourceName);
		}
		configuration.addLoadedResource(resourceName);
	}

	/**
	 * 解析每个SQL
	 * 
	 * @param builderAssistant
	 * @param node
	 */
	public void parseSql(MapperBuilderAssistant builderAssistant, XNode node) {
		String id = builderAssistant.applyCurrentNamespace(node.getStringAttribute("id"), false);

		LanguageDriver langDriver = new XMLLanguageDriver();
		SqlSource sqlSource = langDriver.createSqlSource(configuration, node, Object.class);

		builderAssistant.addMappedStatement(id, sqlSource, StatementType.PREPARED, SqlCommandType.SELECT, null, null,
				null, Object.class, null, null, null, false, true, false, new NoKeyGenerator(), null, null, null,
				langDriver, null);
	}

}
